import 'dart:convert';
import 'dart:io';
import 'dart:math';
import 'dart:typed_data';
import 'package:flutter/material.dart';
import 'package:web_socket_channel/io.dart';
import 'package:web_socket_channel/web_socket_channel.dart';
import 'package:web_socket_channel/status.dart' as web_socket_channel_status;
import 'chat_result_entity.dart';
import 'sound_util.dart';
class ControllerChannel {
  /// 单例实例：通过私有构造函数确保全局唯一，避免多连接冲突
  static final ControllerChannel instance = ControllerChannel._();

  /// 私有构造函数：禁止外部通过 new 关键字创建实例，强制使用单例
  ControllerChannel._();

  /// 帧状态常量定义：标记音频数据帧的传输阶段
  /// - firstFrame：首帧（第一个音频数据包，需携带元数据）
  /// - continueFrame：中帧（中间的音频数据包，仅需携带音频数据）
  /// - lastFrame：尾帧（最后一个数据包，标记传输结束）
  static const int firstFrame = 0;
  static const int continueFrame = 1;
  static const int lastFrame = 2;

  /// WebSocket 通道实例：管理与服务端的长连接， nullable 表示可能未初始化
  WebSocketChannel? _channel;

  /// 当前帧状态：默认从首帧开始，随传输过程切换
  int _status = firstFrame;

  /// 会话ID：用于标识一次完整的音频交互会话（由 _generateFixedLengthNumUuid 生成）
  String _smid = "";

  /// 已注释的配置项：预留的动态配置字段（原设计可能支持动态URL和请求头，当前硬编码）
  // String wssUrl = "";
  // Map<String, String> headers = {};


  /// 发送音频帧到服务端
  /// @param chunk：音频字节数据（Speex编码后的数据），nullable 表示可能无数据（如仅发送结束标记）
  /// @param status：外部指定的帧状态（当前仅处理首帧逻辑，中帧/尾帧由内部状态管理）
  send(Uint8List? chunk, int status, {Function? onError, void Function()? onDone}) async {
    _status = status;
    /// 仅处理首帧逻辑：首帧需初始化WebSocket连接，中帧/尾帧依赖已有连接
    if (status == firstFrame) {
      const wssUrl = "ws://10.16.3.174:8089/speech-chat";

      // 先关闭已有连接：避免重连时出现连接泄露
      await stop();
      debugPrint("开始连接WebSocket");
      // 建立跨平台WebSocket连接：携带身份验证和环境标识头
      _channel = await createCrossPlatformWebSocket(wssUrl, {
        "X-Client-Type": "APP", // 客户端类型：标识为移动应用
        "X-Token-ID": "bc73acea9db7ffc4646cd974203c1234632fb90de43169d1b6b182e980debd0ee44c56dfdfe31eeb5e3c15405ab393c0", // 身份验证Token
        "X-Env": "dev" // 环境标识：开发环境
      });
      // 初始化音频播放器：停止现有播放→启动流式播放，为接收服务端音频做准备
      await SoundUtil.instance.stopStreamPlayer();
      await SoundUtil.instance.startStreamPlayer();

      // 监听服务端消息：接收ASR文本、TTS文本、音频数据等响应
      _channel!.stream.listen((message) {
        _onMessage(message); // 解析消息并转发给回调
      }, onDone: () {
        debugPrint("WebSocket 关闭");
        if (onDone != null) {
          onDone();
        }
      }, onError: (err) {
        debugPrint("WebSocket 错误 ：$err");
        if (onError != null) {
          onError();
        }
      });
    }
    // 处理无音频数据的场景：直接发送尾帧标记结束
    if (chunk == null) {
      _status = lastFrame;
      await _sendFrame(Uint8List(0)); // 发送空数据尾帧
      return;
    }
    // 发送首帧音频数据
    await _sendFrame(chunk);
  }

  /// 结果回调函数：存储外部设置的结果处理逻辑（如将ASR/TTS结果返回给业务层）
  /// late 关键字：延迟初始化，确保在 listen 方法调用前不被访问
  late Function(ChatResultEntity) _callback;

  /// 设置结果回调：让外部业务层监听服务端返回的结果
  /// @param callback：接收 ChatResultEntity 类型的回调函数
  listen(Function(ChatResultEntity) callback) {
    _callback = callback;
  }

  /// 关闭WebSocket连接：释放资源，避免连接泄露
  /// 使用 web_socket_channel_status.normalClosure 表示正常关闭（状态码1000）
  stop() async {
    await _channel?.sink.close(web_socket_channel_status.normalClosure);
  }

  /// 处理服务端消息：解析JSON字符串→转换为实体类→调用回调函数
  /// @param data：服务端返回的JSON格式字符串
  _onMessage(String data) {
    // JSON字符串解析为Map
    final Map<String, dynamic> json = jsonDecode(data);
    // Map转换为业务实体类（ChatResultEntity需包含fromJson工厂方法）
    ChatResultEntity res = ChatResultEntity.fromJson(json);
    // 将结果转发给外部回调
    _callback(res);
  }

  /// 跨平台创建WebSocket连接：适配移动端（Android/iOS）和桌面端（MacOS/Windows/Linux）
  /// @param uri：WebSocket连接地址（wss://开头）
  /// @param headers：请求头（如身份验证、客户端信息）
  /// @return 初始化后的 WebSocketChannel 实例
  /// @throws UnsupportedError：当平台不支持时抛出异常（如Web平台）
  Future<WebSocketChannel> createCrossPlatformWebSocket(String uri, Map<String, String> headers) async {
    if (Platform.isAndroid || Platform.isIOS || Platform.isMacOS || Platform.isWindows || Platform.isLinux) {
      // 原生平台使用 IOWebSocketChannel（基于IO流的实现）
      final webSocket = await WebSocket.connect(uri, headers: headers);
      return IOWebSocketChannel(webSocket);
    } else {
      // 不支持Web平台（Web需使用 HtmlWebSocketChannel）
      throw UnsupportedError('当前平台不支持 WebSocket');
    }
  }

  /// 封装并发送音频帧：根据当前帧状态（首/中/尾）构建不同格式的数据包
  /// @param chunk：Speex编码后的音频字节数据
  Future<void> _sendFrame(Uint8List chunk) async {
    // 存储最终要发送的数据包
    Map<String, dynamic> frame = {};

    // 基础数据段：包含帧状态和Base64编码的音频数据
    final dataSection = {
      "status": _status,
      "audio": base64Encode(chunk), // 音频数据Base64编码（服务端需解码）
    };

    // 1. 处理首帧：需携带会话元数据（如会话ID、编码格式）
    if (_status == firstFrame) {
      // 生成32位数字型会话ID（唯一标识本次交互）
      _smid = _generateFixedLengthNumUuid(32);
      // 首帧数据包格式：metadata（元数据）+ payload（音频数据）
      frame = {
        "metadata": {
          "smid": _smid, // 会话ID
          "requestType": "AUDIO", // 请求类型：音频交互
          "familyId": "7306", // 设备/家庭ID（业务自定义）
          "roomName": "默认房间", // 房间名称（业务自定义）
          "asrEncoding": "speex", // 客户端ASR编码格式（Speex）
          "ttsEncoding": "speex-org-wb;7" // 服务端TTS编码格式（宽频Speex，质量等级7）
        },
        "payload": dataSection
      };
      debugPrint("发送音频-首帧 : $frame");
      // 首帧发送后，切换状态为中帧（后续发送中帧数据）
      _status = continueFrame;
    }
    // 2. 处理中帧：仅需携带音频数据（元数据仅在首帧发送）
    else if (_status == continueFrame) {
      frame = {
        "payload": dataSection
      };
      debugPrint("发送音频-中帧 : $frame");
    }
    // 3. 处理尾帧：仅携带结束状态，无音频数据
    if (_status == lastFrame) {
      frame = {
        "payload": {
          "status": _status, // 仅标记结束状态
        }
      };
      debugPrint("发送音频-尾帧 : $frame");
    }
    // 发送数据包：将Map转换为JSON字符串，通过WebSocket发送
    _channel?.sink.add(jsonEncode(frame));
  }

  /// 生成固定长度的数字型UUID：用于创建会话ID（smid）
  /// @param length：UUID长度（1-32位，超出范围抛异常）
  /// @return 数字组成的字符串（如"12345678901234567890123456789012"）
  /// @throws ArgumentError：当长度不在1-32之间时抛出
  String _generateFixedLengthNumUuid(int length) {
    // 校验长度合法性
    if (length < 1 || length > 32) {
      throw ArgumentError("长度必须在 1-32 之间");
    }

    // 普通随机数生成器（非加密级，适合非安全场景的会话ID）
    final random = Random();
    // 字符串缓冲区：高效拼接随机数字
    final buffer = StringBuffer();

    // 生成length个0-9的随机数字
    for (int i = 0; i < length; i++) {
      buffer.write(random.nextInt(10)); // nextInt(10) 返回 0-9 的整数
    }

    return buffer.toString();
  }
}